// UIImageView+AFNetworking.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import "UIImageView+AFNetworking.h"

#define kCachedImageOnLocalDisk     TRUE
#define kCachedImageFolder          [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"]

@interface AFImageCache : NSCache
@property (strong, nonatomic) NSString *cachedFolder;
- (UIImage *)cachedImageForRequest:(NSURLRequest *)request;
- (void)cacheImage:(UIImage *)image
        forRequest:(NSURLRequest *)request;
@end

#pragma mark -

static char kAFImageRequestOperationObjectKey;

@interface UIImageView (_AFNetworking)
@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFImageRequestOperation *af_imageRequestOperation;
@end

@implementation UIImageView (_AFNetworking)
@dynamic af_imageRequestOperation;
@end

#pragma mark -

@implementation UIImageView (AFNetworking)

- (AFHTTPRequestOperation *)af_imageRequestOperation {
    return (AFHTTPRequestOperation *)objc_getAssociatedObject(self, &kAFImageRequestOperationObjectKey);
}

- (void)af_setImageRequestOperation:(AFImageRequestOperation *)imageRequestOperation {
    objc_setAssociatedObject(self, &kAFImageRequestOperationObjectKey, imageRequestOperation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

+ (NSOperationQueue *)af_sharedImageRequestOperationQueue {
    static NSOperationQueue *_af_imageRequestOperationQueue = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _af_imageRequestOperationQueue = [[NSOperationQueue alloc] init];
        [_af_imageRequestOperationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
    });
    
    return _af_imageRequestOperationQueue;
}

+ (AFImageCache *)af_sharedImageCache {
    static AFImageCache *_af_imageCache = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _af_imageCache = [[AFImageCache alloc] init];
        _af_imageCache.countLimit = 15;
    });
    
    return _af_imageCache;
}

#pragma mark -

- (void)setImageWithURL:(NSURL *)url {
    [self setImageWithURL:url placeholderImage:nil];
}

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholderImage {
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
    [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}

- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
              placeholderImage:(UIImage *)placeholderImage
                       success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
                       failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure {
    [self cancelImageRequestOperation];
    
    UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForRequest:urlRequest];
    if (cachedImage) {
        if (success) {
            success(nil, nil, cachedImage);
        } else {
            self.image = cachedImage;
        }
        
        self.af_imageRequestOperation = nil;
    } else {
        self.image = placeholderImage;
        
        AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
        [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
            if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
                if (success) {
                    self.image = responseObject;
                    success(operation.request, operation.response, responseObject);
                } else if (responseObject) {
                    self.image = responseObject;
                }
                
                if (self.af_imageRequestOperation == operation) {
                    self.af_imageRequestOperation = nil;
                }
            }
            
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // switch to a background thread and perform your expensive operation
                [[[self class] af_sharedImageCache] cacheImage:responseObject forRequest:urlRequest];
            });
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
            if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
                if (failure) {
                    failure(operation.request, operation.response, error);
                }
                
                if (self.af_imageRequestOperation == operation) {
                    self.af_imageRequestOperation = nil;
                }
            }
        }];
        
        self.af_imageRequestOperation = requestOperation;
        
        [[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation];
    }
}

- (void)cancelImageRequestOperation {
    [self.af_imageRequestOperation cancel];
    self.af_imageRequestOperation = nil;
}

- (void)setImageWithURL:(NSURL *)urlRequest
       placeholderImage:(UIImage *)placeholderImage
                success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
                failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure {
    
    if(urlRequest.absoluteString.length > 0) {
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:urlRequest];
        [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
        [self setImageWithURLRequest:request placeholderImage:placeholderImage success:success failure:failure];
    } else {
        self.image = placeholderImage;
        failure(nil, nil, nil);
    }
}

- (void)setImageWithURLString:(NSString *)urlString
             placeholderImage:(UIImage *)placeholderImage
                      success:(void (^)(UIImage *image))success
                      failure:(void (^)(NSError *error))failure {
    
    if(urlString.length > 0) {
        NSURL *url = [NSURL URLWithString:urlString];
        if(url) {
            [self setImageWithURL:url
                 placeholderImage:placeholderImage
                          success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
                              success(image);
                          } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
                              failure(error);
                          }];
        }
    }
}

@end

#pragma mark -

static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) {
    return [[request URL] absoluteString];
}

static inline NSString * HHTImageCacheKeyFromURLRequest(NSURLRequest *request) {
    return[[[request URL] absoluteString] md5Encryption];
}

@implementation AFImageCache
@synthesize cachedFolder;

- (id)init {
    self = [super init];
    if (self) {
        /* Ho Huu Tai: cached on local disk */
        if (kCachedImageOnLocalDisk) {
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *documentDirectory = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
            self.cachedFolder = [NSString stringWithFormat:@"%@/%@",documentDirectory, kCachedImageFolder];
            
            BOOL isDir = FALSE;
            BOOL isExist = [[NSFileManager defaultManager] fileExistsAtPath:cachedFolder isDirectory:&isDir];
            if (!isExist && !isDir) {
                [[NSFileManager defaultManager] createDirectoryAtPath:cachedFolder withIntermediateDirectories:YES attributes:nil error:nil];
            }
            
        }
    }
    return self;
}

- (UIImage *)cachedImageForRequest:(NSURLRequest *)request {
    switch ([request cachePolicy]) {
        case NSURLRequestReloadIgnoringCacheData:
        case NSURLRequestReloadIgnoringLocalAndRemoteCacheData:
            return nil;
        default:
            break;
    }
    
	UIImage *cachedImage = [self objectForKey:AFImageCacheKeyFromURLRequest(request)];
    
    /* Ho Huu Tai : cached image on local disk */
    if (kCachedImageOnLocalDisk) {
        if (!cachedImage) {
            NSString *encodeName = HHTImageCacheKeyFromURLRequest(request);
            cachedImage = [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@",cachedFolder,encodeName]];
            
            //cached in in NSCache
            if (cachedImage) {
                [self setObject:cachedImage forKey:AFImageCacheKeyFromURLRequest(request)];
            }
        }
    }
    
    return cachedImage;
}

- (void)cacheImage:(UIImage *)image
        forRequest:(NSURLRequest *)request {
    if (image && request) {
        [self setObject:image forKey:AFImageCacheKeyFromURLRequest(request)];
        
        /* Ho Huu Tai: cached image on local disk*/
        if (kCachedImageOnLocalDisk) {
            NSString *encodeName = HHTImageCacheKeyFromURLRequest(request);
            NSString *fileName = [NSString stringWithFormat:@"%@/%@",cachedFolder,encodeName];
            NSError *error = nil;
            [UIImagePNGRepresentation(image) writeToFile:fileName options:NSDataWritingAtomic error:&error];
        }
    }
}

@end

#endif
